package com.cy.common.aspect;

import com.cy.commom.annotation.RequiredLog;
import com.cy.commom.utils.IPUtils;
import com.cy.dbsys.pojo.SysLog;
import com.cy.dbsys.pojo.SysUser;
import com.cy.dbsys.service.SysLogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class SysLogAspect {
    @Autowired
    private SysLogService sysLogService;

    /**
     * 切入点定义：
     *  1）使用@PointCut注解进行切入点描述
     *  2）使用bean表达式定义切入点，语法bean（spring容器中管理的某个bean的名字）
     *      bean表达式是一种粗粒度的切入点表达式（不能精确到具体bean中的那个方法）
     */
    @Pointcut("@annotation(com.cy.commom.annotation.RequiredLog)")
    public void doLog(){}

    @Around("doLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long t1 = System.currentTimeMillis();
        Object result = joinPoint.proceed();//执行目标方法
        long t2 = System.currentTimeMillis();
        long time = t2-t1;//执行时间
        doSaveLog(joinPoint,time);
        return result;//目标方法执行的结果
    }
    /**
     * 记录用户正常行为日志需要；
     * username:当前执行的用户名
     * ip；执行的地址
     * operation:执行的操作
     * method：执行的方法（一般为全类名加方法名）
     * params：操作时传的参数
     * time：执行时间
     * createdTime；多久执行的
     */
    private void doSaveLog(ProceedingJoinPoint joinPoint,long time) throws NoSuchMethodException, JsonProcessingException {
        //1、获取用户日志
            //1.1获取ip
            String ip = IPUtils.getIpAddr();
            //1.2获取当前用户名 ?=>getPrincipal()
            SysUser user=(SysUser) SecurityUtils.getSubject().getPrincipal();
            String username = user.getUsername();
            //1.3获取目标对象类型，基于此类型获取目标方法对象
            Class<?> targetClass = joinPoint.getTarget().getClass();

            //targetClass= class com.cy.dbsys.service.impl.SysUserServiceImpl
            System.out.println("targetClass="+targetClass);

            //1.4获取方法签名，基于方法签名获取目标方法
            MethodSignature signature = (MethodSignature)joinPoint.getSignature();
            //signature==PageObject com.cy.dbsys.service.impl.SysUserServiceImpl.findPageObjects(String,Integer)
            System.out.println("signature=="+signature);

            //都是findPageObjects
//            System.out.println("signature.getMethod().getName()=="+signature.getMethod().getName());
//            System.out.println("signature.getName()=="+signature.getName());

            //targetMethod=public com.cy.commom.pojo.PageObject com.cy.dbsys.service.impl.SysUserServiceImpl.findPageObjects(java.lang.String,java.lang.Integer)
            Method targetMethod = targetClass.getDeclaredMethod(signature.getName(), signature.getParameterTypes());

            //1.5基于目标方法对象获取目标方法上的requiredLog注解
            RequiredLog requiredLog = targetMethod.getAnnotation(RequiredLog.class);

            //1.6基于注解获取操作属性operation
            String operation = requiredLog.operation();
            String method = targetClass.getName()+"."+targetMethod.getName();//目标方法
            String params = new ObjectMapper().writeValueAsString(joinPoint.getArgs());//(执行目标方法时传入的实际参数值)
        //2、封装用户行为日志
        SysLog log = new SysLog();
        log.setIp(ip);
        log.setUsername(username);
        log.setOperation(operation);
        log.setMethod(method);
        log.setParams(params);
        log.setTime(time);
        //3、保存日志
        sysLogService.saveObject(log);
    }
}
